#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    Part of Seagull - The Pyhon Pages engine
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    SystemConfig:
      Representation of the config.xml file
"""
__author__ = 'Jonathan Slenders <jonathan at slenders dot be>'
__license__ = 'GNU LGPL'

__all__ = ('SystemConfig', 'SeagullConfigVariableNotSetException')

import xml.dom.minidom
import sys
import os
import socket

import sessions
import page_providers
import loggers


class SystemConfig(object):
	"""
	Singleton implementation for system configuration
	"""
	__instance = None

	def __init__(self):
		if SystemConfig.__instance is None:
			SystemConfig.__instance = _SystemConfig()

		self.__dict__['_SystemConfig__config'] = SystemConfig.__instance

	def __getattr__(self, attr):
		""" Delegate access to implementation """
		return getattr(SystemConfig.__instance, attr)

	def __setattr__(self, attr, value):
		""" Delegate access to implementation """
		return setattr(SystemConfig.__instance, attr, value)


class _SystemConfig(object):
	"""
	System config configuration
	"""
	def __init__(self):
		# Default configuration settings
		self.port = 80
		self.page_providers = [ ]
		self.loggers = [ ]
		self.session_manager = None
		self.error_pages = { }
		self.domain = 'localhost'
		self.url_prefix = ''
		self.auto_compile = False
		self.debug = False # Compile/execute with debug symbols

		# Read config from SEAGULL_CONFIG environment variable
		try:
			config = os.environ['SEAGULL_CONFIG']
		except KeyError:
			raise SeagullConfigVariableNotSetException()

		self.read_config(config)

	def read_config(self, filename):
		assert filename[-4:] == '.xml'

		# Read root element from XML config
		self._dom = xml.dom.minidom.parse(os.path.join(filename))
		config = self._dom.getElementsByTagName('python-pages-config')[0]

		def read_content(node):
			""" Read textual content of an element node """
			text = []
			for n in node.childNodes:
				if n.nodeType in (node.TEXT_NODE, node.CDATA_SECTION_NODE):
					text.append(n.data)
			return u''.join(text)

		def read_parameters(node):
			""" Read <parameter name="...">...</parameter> childs of an element node """
			params = { }
			for parameter in node.getElementsByTagName('parameter'):
				key = str(parameter.getAttribute('name')).replace('-', '_')
				params[key] = read_content(parameter)
			return params

		def read_section(section):
			"""
			Read settings (a configuration file can have multiple sections,
			this method should be called for every active section.)
			"""
			for port in section.getElementsByTagName('port'):
				self.port = int(read_content(port))

			for domain in section.getElementsByTagName('domain'):
				self.domain = read_content(domain)

			for auto_compile in section.getElementsByTagName('auto-compile'):
				c = read_content(auto_compile).lower()
				assert c in ('true', 'false'), Exception('auto-compile should be true or false')
				self.auto_compile = (c == 'true')

			for debug in section.getElementsByTagName('debug'):
				c = read_content(debug).lower()
				assert c in ('true', 'false'), Exception('debug should be true or false')
				self.debug = (c == 'true')

			for url_prefix in section.getElementsByTagName('url-prefix'):
				self.url_prefix = read_content(url_prefix).lower()

			for page_provider in section.getElementsByTagName('page-provider'):
				name = page_provider.getAttribute('name')
				class_ = page_provider.getAttribute('class')
				params = read_parameters(page_provider)
				params['name'] = name
				try:
					self.page_providers.append(
						getattr(page_providers, class_) (**params))
				except TypeError, e:
					print 'Error: Unable to create instance of page provider (named %s), %s', (name, str(e))
					raise

			for session_manager in section.getElementsByTagName('session-manager'):
				class_ = session_manager.getAttribute('class')
				name = session_manager.getAttribute('name')
				params = read_parameters(session_manager)
				self.session_manager = getattr(sessions, class_) (**params)

			for logger in section.getElementsByTagName('logger'):
				class_ = logger.getAttribute('class')
				name = logger.getAttribute('name')
				params = read_parameters(logger)
				params['name'] = name
				self.loggers.append(
					getattr(loggers, class_) (**params))
				
			for error_page in section.getElementsByTagName('error-page'):
				code = error_page.getAttribute('code')
				path = read_content(error_page.getElementsByTagName('path')[0])
				params = read_parameters(error_page)
				self.error_pages[int(code)] = ErrorPage(path, params)

		# For every <host> section
		for host in config.getElementsByTagName('host'):
			# Default settings
			if host.getAttribute('hostname') == '*':
				read_section(host)

			# Specific for this host
			if host.getAttribute('hostname') == socket.gethostname():
				read_section(host)

class ErrorPage(object):
	"""
	Error page structure for the system config
	"""
	def __init__(self, page_path, parameters):
		self.page_path = page_path
		self.parameters = parameters

class SeagullConfigVariableNotSetException(Exception):
	def __init__(self):
		Exception.__init__(self, 'Environment variable SEAGULL_CONFIG not set')

